package com.lai.laiojcodesandbox.language.codesandbox;

import cn.hutool.core.date.StopWatch;
import cn.hutool.core.util.ArrayUtil;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.async.ResultCallback;
import com.github.dockerjava.api.command.*;
import com.github.dockerjava.api.model.*;
import com.github.dockerjava.core.command.ExecStartResultCallback;
import com.lai.laiojcodesandbox.CodeSandboxTemplate;
import com.lai.laiojcodesandbox.model.ExecuteMessage;
import com.lai.laiojcodesandbox.utils.ProcessUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.Closeable;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;

@Slf4j
@Component
public class JavaCodeSandbox extends CodeSandboxTemplate {
    /**
     * 编译代码，获取 class 文件
     *
     * @param userCodeFile
     * @return
     */
    @Override
    public ExecuteMessage compileFile(File userCodeFile) {
        // 获取编译代码的执行语句
        String complieCmd = String.format("javac -encoding utf-8 %s", userCodeFile.getAbsolutePath());
        try {
            // 执行 编译代码
            Process complieProcess = Runtime.getRuntime().exec(complieCmd);
            ExecuteMessage executeMessage = ProcessUtils.runProcessAndGetMessage(complieProcess, "编译");
            System.out.println(executeMessage);
            // 判断是否编译成功
            if (executeMessage.getExitValue() != 0) {
                throw new RuntimeException("编译错误");
            }
            System.out.println("编译成功");
            return executeMessage;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 执行文件
     *
     * @param useCodeFile
     * @param inputList
     * @return
     */
    @Override
    public List<ExecuteMessage> runFile(File useCodeFile, List<String> inputList, DockerClient dockerClient, String containerId, String dirName) {

        // 5. 创建运行命令
        // 创建运行信息集合
        List<ExecuteMessage> executeMessageList = new ArrayList<>();
        String parentPath = useCodeFile.getParentFile().getAbsolutePath();
        System.out.println("文件存放目录为：" + parentPath);
        String dirPath = "/app/" + dirName;


        // 5.1 遍历输入用例
        for (String inputArgs : inputList) {
            StopWatch stopWatch = new StopWatch();
            // 拼接命令
            String[] inputArgsArray = inputArgs.split(" ");
            String[] cmdArray = ArrayUtil.append(new String[]{"java", "-cp", dirPath, "Main"}, inputArgsArray);
            System.out.println(Arrays.toString(cmdArray));
            // 创建执行命令
            ExecCreateCmdResponse execCreateCmdResponse = dockerClient.execCreateCmd(containerId)
                    .withCmd(cmdArray)
                    // 错误输出
                    .withAttachStderr(true)
                    // 标准输入
                    .withAttachStdin(true)
                    // 标准输出
                    .withAttachStdout(true)
                    .exec();
            System.out.println("创建执行命令为：" + execCreateCmdResponse);
            String execId = execCreateCmdResponse.getId();

            ExecuteMessage executeMessage = new ExecuteMessage();
            final String[] errorMessage = {null};
            final String[] message = {""};
            AtomicLong time = new AtomicLong(0L);
            // 判断是否超时
            final Boolean[] timeout = {true};
            // 创建异步回调函数
            ExecStartResultCallback execStartResultCallback = new ExecStartResultCallback() {
                @Override
                public void onComplete() {
                    // 如果执行完成，则表示没超时
                    System.out.println("执行完毕");
                    timeout[0] = false;
                    super.onComplete();
                }

                @Override
                public void onNext(Frame frame) {
                    // 获取返回流类型
                    StreamType streamType = frame.getStreamType();
                    // 判断是否为错误信息
                    if (StreamType.STDERR.equals(streamType)) {
                        errorMessage[0] = new String(frame.getPayload());
                        System.out.println("输出错误结果：" + errorMessage[0]);
                    } else {
                        message[0] += new String(frame.getPayload());
                        System.out.println("输出结果：" + message[0]);
                    }
                    super.onNext(frame);
                }
            };

            final long[] maxMemory = {0L};
            // 获取占用内存
            StatsCmd statsCmd = dockerClient.statsCmd(containerId);


            // 创建获取内存回调函数
            statsCmd.exec(new ResultCallback<Statistics>() {
                @Override
                public void onNext(Statistics statistics) {
                    Long memoryUsage = statistics.getMemoryStats().getUsage();
                    maxMemory[0] = Math.max(memoryUsage, maxMemory[0]);
                }
                @Override
                public void onStart(Closeable closeable) {}
                @Override
                public void onError(Throwable throwable) {}
                @Override
                public void onComplete() {}
                @Override
                public void close() {}
            });
            statsCmd.close();

            ExecutorService executor = Executors.newSingleThreadExecutor();

            // 假设你有一个函数需要被调用，并且这个函数没有返回值
            Future<Void> future = executor.submit(() -> {
                // 调用你的函数或方法
                stopWatch.start();
                dockerClient.execStartCmd(execId)
                        .exec(execStartResultCallback).awaitCompletion();
                stopWatch.stop();
                time.set(stopWatch.getLastTaskTimeMillis());
                return null; // 因为没有返回值，所以返回null
            });

            try {
                // 等待任务完成，最多等待10秒
                future.get(1000, TimeUnit.SECONDS);
                System.out.println("容器任务成功完成");
            } catch (TimeoutException e) {
                // 任务超时，尝试取消任务
                future.cancel(true); // 第二个参数表示如果任务还在运行，是否应该中断它
                System.out.println("容器执行时间超时");
            }catch ( InterruptedException | ExecutionException e){
                e.printStackTrace();
            } finally {
                executor.shutdown(); // 关闭ExecutorService
            }

            executeMessage.setErrorMessage(errorMessage[0]);
            executeMessage.setMessage(message[0]);
            executeMessage.setTime(time.get());
            executeMessage.setMemory(maxMemory[0]);
            executeMessageList.add(executeMessage);
        }
        return executeMessageList;
    }
}
